浅谈 GORM 默认值处理 您所在的位置:网站首页 gorm 更新插入 浅谈 GORM 默认值处理

浅谈 GORM 默认值处理

2024-01-13 01:58| 来源: 网络整理| 查看: 265

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

在 gorm 使用的时候,很多同学会很纠结,发现默认值的更新总是不太对,明明数据库声明了 create_time,加上了 default 值,但最后插入 db 的却还是零值。

今天这篇,我们来看看在 gorm 中怎么使用默认值。

default 标签 type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"` }

gorm 支持这样的 default 标签,直接声明字段的默认值。这样在插入记录到数据库时,默认值就会被用于填充值为 零值 的字段。

比如上面的 Name,如果我们留空,在 gorm 生成 sql 时就会被转为 "galeone"。

这一点的支持在 schema.ParseField 里面,首先还是经典的用反射取 tag 的操作,拿到 gorm 对应的 tagSettings。我们要用的 default 就在其中。

tagSetting = ParseTagSetting(fieldStruct.Tag.Get("gorm"), ";") func ParseTagSetting(str string, sep string) map[string]string { settings := map[string]string{} names := strings.Split(str, sep) for i := 0; i < len(names); i++ { j := i if len(names[j]) > 0 { for { if names[j][len(names[j])-1] == '\\' { i++ names[j] = names[j][0:len(names[j])-1] + sep + names[i] names[i] = "" } else { break } } } values := strings.Split(names[j], ":") k := strings.TrimSpace(strings.ToUpper(values[0])) if len(values) >= 2 { settings[k] = strings.Join(values[1:], ":") } else if k != "" { settings[k] = k } } return settings }

有了 tagSettings ,我们就可以找到 default 这个子标签,并拿到默认值:

if v, ok := field.TagSettings["DEFAULT"]; ok { field.HasDefaultValue = true field.DefaultValue = v }

随后对默认值进行 trim,并用反射,转换 默认值的类型: image.png

最后在 ConvertToCreateValues 函数里,判断如果是零值,就用这里的 DefaultValueInterface 的值:

image.png

整体实现并不复杂,大家可以参照源码了解一下。

从一个开发者的角度,我们只需要记住一开始的例子,用 gorm:"default:galeone" 来传入默认值即可。

默认值为什么不生效

有些同学很费解,为什么我没有声明 default 标签,默认值就不生效了。

笔者自己就遇见过这种case,在 model 里我们定义了 int, bool 等基本类型,将其从 0 改为 1 时可以正常插入,但从 1 改为 0 就不生效了。

这一点很 trick,本质在于,golang 对于基础类型都是有零值的。也就意味着,当你没有给 struct 中的部分 field 赋值时,默认的零值会干扰判断。

到底是你刻意不去赋值,希望用 0,还是只是漏了传呢?

这样的语义是比较混乱的。所以 gorm 的哲学在于,对于零值,统一认为没有传,不会插入到数据库。

在更新数据时,如果使用了 struct 来更新数据,默认只会更新非零值字段,如果使用map更新数据,则会更新全部字段,在使用 struct 更新时,也可以使用 Select 方法来选择想要更新的字段,在这种情况下,零值/非零值字段都会更新.

对于声明了默认值的字段,像 0、''、false 等零值是不会保存到数据库。您需要使用指针类型或 Scanner/Valuer 来避免这个问题.

type User struct { gorm.Model Name string Age *int `gorm:"default:18"` Active sql.NullBool `gorm:"default:true"` }

当我们声明为指针后,就可以和零值区分开了,毕竟指针的零值对应的是 nil。

// UPDATE users SET name='new_name', age=0 WHERE id=111; DB.Model(&result).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0}) // UPDATE users SET name='hello', updated_at = '2013-11-17 21:34' WHERE id = 1 db.Model(&user).Updates(User{Name: "hello", Age: 0, Active: false}) 如何用数据库的默认值

我有一个model,在db里面设置了默认值,怎么让gorm不要给这个字段在 insert 的时候设置go的 zero value

前面我们介绍过,尤其是对于 createTime, updateTime 这类字段,数据库里我们会声明 default 使用 current timestamp。

此时我们不希望用 gorm 默认的,而是希望它放过去,直接走到 DB,这时候又该怎么处理呢?

gorm 为此提供了一个 default:(-) 标签,加上它,当我们留零值时,gorm 不会额外处理,就可以使用 DB 默认的声明了。

type User struct { ID string `gorm:"default:uuid_generate_v3()"` // db func FirstName string LastName string Age uint8 FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"` }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有